//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The basic module for MapWindow version 6.0
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is from MapWindow.dll version 6.0
//
// The Initial Developer of this Original Code is Ted Dunsford. Created 3/8/2008 4:44:51 PM
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using MapWindow.DirectX.Components;
using MapWindow.Drawing;
using MapWindow.Forms;
using MapWindow.Main;
using MapWindow.Geometries;

namespace MapWindow.DirectX
{


    /// <summary>
    /// Layer
    /// </summary>
    [ToolboxItem(false)]
    public class LayerDX : Layer, ILayerDX
    {

        #region LayerDX

      


        #region Private Variables



        private object _editCopy;
        private ICollection<ILayer> _container; // contains this 
        private DrawBox _drawBox;
        private ILegendItem _parentItem;
        private int _numParts = 1;
        private int _numStages = 1;
        #endregion

        #region Constructors


        /// <summary>
        /// Creates a new Layer, but this should be done from the derived classes
        /// </summary>
        protected LayerDX()
        {
            InitializeComponent();
            Configure();
        }

        /// <summary>
        /// Creates a new Layer, but this should be done from the derived classes
        /// </summary>
        /// <param name="container">The container this layer should be a member of</param>
        protected LayerDX(ICollection<ILayer> container):base(container)
        {
            _container = container;
            ILayerCollectionDX lc = container as ILayerCollectionDX;
            _parentItem = lc.MapFrame as ILegendItem;
            if(container != null) container.Add(this);
           
            InitializeComponent();
            Configure();
        }

        private void Configure()
        {
            
            // New Drawing Stuff
            IsVisible = true;
        }

        ///// <summary>
        ///// Gets or sets the Components belonging to this layer, if any
        ///// </summary>
        //protected IContainer Components
        //{
        //    get { return components; }
        //    set { components = value; }

        //}



        #endregion

        #region Methods


       

        #endregion

     

        #region Protected Methods

        /// <summary>
        /// Sets up the camera, changing the perspective to allow zooming in past the precision allowed by floats
        /// </summary>
        /// <param name="e">A DrawSetupArgs event argument</param>
        protected virtual void OnDrawSetup(DrawSetupArgs e)
        {
            if(_drawBox == null || e.Camera.DrawBox.Equals(_drawBox) == false)
            {
                IsInitialized = false;
                _drawBox = e.Camera.DrawBox;
            }
            
        }
      

       
        ///// <summary> 
        ///// Clean up any resources being used.
        ///// </summary>
        ///// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        //protected override void Dispose(bool disposing)
        //{
        //    // The Subtypes of layers will simply override this one since they have to control the special instance of unmanaged variables
        //    if (disposing && (components != null))
        //    {

        //        components.Dispose();
        //    }

        //}


       
       


     

        #endregion


        #region EventHandlers

        void Property_ChangesApplied(object sender, EventArgs e)
        {
            if (_editCopy != null)
            {
                CopyProperties(_editCopy);
            }
            OnItemChanged();
        }


        private void ShowProperties_Click(object sender, EventArgs e)
        {
            // Allow subclasses to prevent this class from showing the default dialog
            HandledEventArgs result = new HandledEventArgs(false);
            OnShowProperties(result);
            if (result.Handled) return;


            PropertyDialog frm = new PropertyDialog();
            _editCopy = this.MemberwiseClone(); // not sure if this works correctly for derived classes
            frm.PropertyGrid.SelectedObject = _editCopy;
         
            frm.ChangesApplied += new EventHandler(Property_ChangesApplied);

            if (frm.ShowDialog() == DialogResult.OK) OnItemChanged();

        }

      
       
       



        #endregion

      

     

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {

        }

        #endregion

        #region Static Methods

        /// <summary>
        /// Opens a filename using the default layer provider and returns a new layer.  The layer will not automatically have a container or be added to a map.
        /// </summary>
        /// <param name="filename">The string filename of the layer to open</param>
        /// <returns>An ILayer interface</returns>
        public new static ILayerDX OpenFile(string filename)
        {
            return LayerManagerDX.DefaultLayerManager.OpenLayer(filename);
        }

        /// <summary>
        /// Opens a filename using the default layer provider and returns a new layer.  The layer will not automatically have a container or be added to a map.
        /// </summary>
        /// <param name="filename">The string filename of the layer to open</param>
        /// <param name="progressHandler">An IProgresshandler that overrides the Default Layer Manager's progress handler</param>
        /// <returns>An ILayer interface with the new layer.</returns>
        public new static ILayerDX OpenFile(string filename, IProgressHandler progressHandler)
        {
            return LayerManagerDX.DefaultLayerManager.OpenLayer(filename, progressHandler);
        }

        /// <summary>
        /// Opens a new layer and automatically adds it to the specified container.
        /// </summary>
        /// <param name="filename">A String filename to attempt to open.</param>
        /// <param name="container">The container (usually a LayerCollection) to add to</param>
        /// <returns>The layer after it has been created and added to the container</returns>
        public new static ILayerDX OpenFile(string filename, ICollection<ILayer> container)
        {
            ILayerManagerDX dm = LayerManagerDX.DefaultLayerManager;
            return dm.OpenLayer(filename, container);
        }

        /// <summary>
        /// Attempts to call the open filename method for any ILayerProvider plugin
        /// that matches the extension on the string.
        /// </summary>
        /// <param name="filename">A String filename to attempt to open.</param>
        /// <param name="inRam">A boolean value that if true will attempt to force a load of the data into memory.  This value overrides the property on this LayerManager.</param>
        /// <param name="container">A container to open this layer in</param>
        /// <param name="progressHandler">Specifies the progressHandler to receive progress messages.  This value overrides the property on this LayerManager.</param>
        /// <returns>An ILayer</returns>
        public new virtual ILayerDX OpenLayer(string filename, bool inRam, ICollection<ILayer> container, IProgressHandler progressHandler)
        {
            ILayerManagerDX dm = LayerManagerDX.DefaultLayerManager;
            return dm.OpenLayer(filename, inRam, container, progressHandler, -1);
        }

      

        #endregion

        #endregion



        #region RenderBase

        #region Events

        /// <summary>
        /// Occurs immediately before drawing, allowing it to be cancelled
        /// </summary>
        public event EventHandler<DrawVerifyArgsDX> BeforeDrawing;

        /// <summary>
        /// Occurs after drawing, indicating if an exception or cancellation occured
        /// </summary>
        public event EventHandler<DrawCompletedArgsDX> DrawingCompleted;


     
        /// <summary>
        /// Occurs when the data has been loaded into the model after the first drawing method
        /// </summary>
        public event EventHandler<DrawArgsDX> Inititialized;

    
       

        #endregion

        #region Private Variables

      



        #endregion

      
        #region Methods

      
        

        /// <summary>
        /// Causes the layer or object to draw itself to the map.  This may initiate
        /// one-time loading of buffers, but should not do heavy calculations every
        /// cycle.
        /// </summary>
        /// <param name="args">A Drawing Args structure.  This is used so that this method
        /// can also be called asynchronously</param>
        public virtual void Draw3D(DrawArgsDX args)
        {
           // try
           // {
                if (IsVisible == false)
                {
                    OnDrawingCompleted(new DrawCompletedArgsDX(args, false, null));
                    return;
                }
                if (OnBeforeDrawing(new DrawVerifyArgsDX(args, false)) == true)
                {
                    OnDrawingCompleted(new DrawCompletedArgsDX(args, true, null));
                    return;
                }

                OnDrawSetup(new DrawSetupArgs(args.Camera));
                args.Camera.Position(args.Device);
                
                if (IsInitialized == false)
                {
                    Initialize(args);
                }

               


                for (int stage = 0; stage < NumStages; stage++)
                {
                    for (int part = 0; part < NumParts; part++)
                    {
                        DrawArgsDX innerArgs = new DrawArgsDX(args.Device, args.Camera, part, stage);
                        OnDraw(innerArgs);
                    }
                }
                OnDrawingCompleted(new DrawCompletedArgsDX(args, false, null));
            //}
            //catch (Exception ex)
            //{
            //    OnDrawingCompleted(this, new DrawCompletedArgsDX(args, false, ex));
            //}


        }

        /// <summary>
        /// This handles the initialization where structures are created
        /// </summary>
        public virtual void Initialize(DrawArgsDX args)
        {
            // setup occurs here
            OnInitialize(args);
        }



        /// <summary>
        /// Determines the geographic distance to use for something that is one pixel wide on the screen
        /// </summary>
        /// <param name="args">A DrawArgsDX with a Microsoft.DirectX.Device</param>
        /// <returns>A float multiplier for the scale</returns>
        public float GetScale(DrawArgsDX args)
        {
            Viewport vp = args.Device.Viewport;
            Transforms t = args.Device.Transform;
            // Find the point along the view path of the camera that intersects with the 0 elevation of the z plane
            //Vector3 pt = Plane.IntersectLine(args.Camera.XYPlane, args.Camera.CameraPosition, args.Camera.TargetPosition);
            Vector3 pt = Vector3.Subtract(args.Camera.TargetPosition, args.Camera.CameraPosition);
            float z = Math.Abs(args.Camera.CameraPosition.Z);
            pt.X = pt.X * z / pt.Z;
            pt.Y = pt.Y * z / pt.Z;
            pt = Vector3.Add(args.Camera.CameraPosition, pt);
            pt.Z = 0;

            // Project that into screen space in order to determine where the 0-elevation plane is between the near plane an the far plane of the scene
            Vector3 depth = Vector3.Project(pt, vp, t.Projection, t.View, t.World);

            // Back project two pixels from the screen into world coordinates that are separated by a distance of 1 pixel
            Vector3 center = new Vector3(vp.Width / 2, vp.Height / 2, depth.Z);
            Vector3 right = new Vector3(vp.Width / 2 + 1, vp.Height / 2, depth.Z);
            Vector3 wCenter = Vector3.Unproject(center, vp, args.Device.Transform.Projection, args.Device.Transform.View, args.Device.Transform.World);
            Vector3 wRight = Vector3.Unproject(right, vp, args.Device.Transform.Projection, args.Device.Transform.View, args.Device.Transform.World);
            
            // The distance between the two locations in world coordinates gives a scale value.
            Vector3 diff = Vector3.Subtract(wRight, wCenter);
           
            return diff.Length();
        }

       

        #endregion

        #region Properties

      


        /// <summary>
        /// Gets the number of parts in this drawing structure
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual int NumParts
        {
            get { return _numParts; }
            protected set { _numParts = value; }
        }

        /// <summary>
        /// Gets the number of stages that will be drawn.  Each stage cycles through every part.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual int NumStages
        {
            get { return _numStages; }
            protected set { _numStages = value; }
        }




        #endregion

        #region Protected Methods



      
        /// <summary>
        /// Fires the event that occurs before drawing to a directX map
        /// </summary>
        /// <param name="e">The DrawVerifyArgsDX parameter with information about the drawing</param>
        /// <returns>Boolean, that if true should cancel the draw event.</returns>
        protected virtual bool OnBeforeDrawing(DrawVerifyArgsDX e)
        {
            if (BeforeDrawing == null) return false;
            BeforeDrawing(this, e);
            return e.Cancel;
        }

       

        /// <summary>
        /// This is the internal drawing method that follows the standard checks.  Drawing is evoked
        /// through the Draw3D command, but internally implemented in the DrawPart method.  After
        /// the initial checks, the Draw3D method will cycle the part integer from 0 to NumParts -1.
        /// </summary>
        /// <param name="e">A MapWindow.DirectX.Drawing.DrawArgsDXclass with the device and camera.</param>
        protected virtual void OnDraw(DrawArgsDX e)
        {
            // Meshes might have several parts to be drawn, so core drawing is done here.
            // This is not public because we do the basic checks against visible and 
            // initialize first.  This is overridden in sub-classes.
        }

        /// <summary>
        /// Fires an event when we are done with the drawing code, whether the drawing was successful, cancelled, or threw an exception.
        /// </summary>
        /// <param name="e">A DrawCompletedArgsDX parameter containing information about the drawing</param>
        protected virtual void OnDrawingCompleted(DrawCompletedArgsDX e)
        {
            if (DrawingCompleted == null) return;
            DrawingCompleted(this, e);
        }

       

        /// <summary>
        /// Fires the Initialized event
        /// </summary>
        /// <param name="e">An EventArgs parameter</param>
        protected virtual void OnInitialize(DrawArgsDX e)
        {
            IsInitialized = true;
            if (Inititialized != null)
            {
                Inititialized(this, e);
            }
            
        }

     

        #endregion

        #endregion

    }




}
